package zelix.hack.hacks;

import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemSword;
import net.minecraft.network.play.client.CPacketPlayer;
import net.minecraft.network.play.client.CPacketPlayerDigging;
import net.minecraft.network.play.client.CPacketUseEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.client.event.EntityViewRenderEvent.CameraSetup;
import net.minecraftforge.client.event.RenderGameOverlayEvent.Text;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import zelix.eventapi.event.EventPlayerPre;
import zelix.hack.Hack;
import zelix.hack.HackCategory;
import zelix.managers.FontManager;
import zelix.managers.HackManager;
import zelix.utils.*;
import zelix.utils.*;
import zelix.utils.system.Connection;
import zelix.value.BooleanValue;
import zelix.value.Mode;
import zelix.value.ModeValue;
import zelix.value.NumberValue;

import java.awt.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class KillAura extends Hack {

	public ModeValue priority;
	public ModeValue mode;
	public BooleanValue walls;
	public BooleanValue autoDelay;
	public BooleanValue packetReach;
	public NumberValue minCPS;
	public NumberValue maxCPS;
	public NumberValue packetRange;
	public NumberValue range;
	public NumberValue FOV;
	public BooleanValue rotate;

//Multi

	boolean send = false;

	public TimerUtils timer;
	public static EntityLivingBase target;
	public List<EntityLivingBase> targets;
	public static float[] facingCam = null;
	public float[] facing = null;
	Vec3d randomCenter = null;
	boolean phaseOne = false;
	boolean phaseTwo = false;
	boolean phaseThree = false;

	boolean fakephaseOne, fakephaseTwo, fakephaseThree = false;

	public static boolean blockstate;



	FontManager fontManager = new FontManager();

	public KillAura() {
		super("KillAura", HackCategory.COMBAT);

		this.priority = new ModeValue("Priority", new Mode("Closest", true), new Mode("Health", false));
		this.mode = new ModeValue("Mode", new Mode("Simple", true), new Mode("AAC", false), new Mode("Legit" , false),
				new Mode("Multi", false),
				new Mode("Fake", false));
		walls = new BooleanValue("ThroughWalls", false);
		autoDelay = new BooleanValue("AutoDelay", false);
		rotate = new BooleanValue("Rotate", false);
		packetReach = new BooleanValue("PacketReach", false);
		packetRange = new NumberValue("PacketRange", 10.0D, 1.0D, 100D);
		minCPS = new NumberValue("MinCPS", 4.0D, 1.0D, 30.0D);
		maxCPS = new NumberValue("MaxCPS", 8.0D, 1.0D, 30.0D);
		range = new NumberValue("Range", 3.4D, 1.0D, 7.0D);
		FOV = new NumberValue("FOV", 180D, 1.0D, 180D);

		this.addValue(mode, priority, walls, autoDelay, packetReach, minCPS, maxCPS, packetRange, range, FOV, rotate);

		this.timer = new TimerUtils();
	}

	@Override
	public String getDescription() {
		return "Attacks the entities around you.";
	}

	@Override
	public void onEnable() {
		facingCam = null;
		if (mode.getMode("AAC").isToggled() || mode.getMode("Fake").isToggled()) {
			facing = null;
			randomCenter = null;
			phaseOne = false;
			phaseTwo = false;
			phaseThree = false;

			fakephaseOne = false;
			fakephaseTwo = false;
			fakephaseThree = false;
		}
		super.onEnable();
	}

	@Override
	public void onDisable() {
		this.facingCam = null;
		this.target = null;
		AutoShield.block(false);
		blockstate = false;
		super.onDisable();
	}

	/*
	 * 
	 * coded by zenwix 2020/11/7 rotation
	 * 
	 * 
	 *
	 */

	@Override
	public void onPlayerEventPre(EventPlayerPre event) {
		Utils.nullCheck();
		if (mode.getMode("Fake").isToggled()) {
			rotate.setValue(false);
			killAuraUpdate();
			FakekillAuraAttack(target, event);
			
		}

		if (rotate.getValue()) {
			if (target != null) {
				event.setPitch(Utils.getRotationsNeeded(target)[1]);
				event.setYaw(Utils.getRotationsNeeded(target)[0]);
				Wrapper.INSTANCE.player().setRotationYawHead(event.getYaw());
				Wrapper.INSTANCE.player().renderYawOffset = event.getYaw();
			}
		}
		super.onPlayerEventPre(event);
	}

	@Override
	public boolean onPacket(Object packet, Connection.Side side) {
		if (side == Connection.Side.OUT) {
			if (packet instanceof CPacketPlayer) {
				Field field = ReflectionHelper.findField(CPacketPlayer.class,
						new String[] { "rotating", "field_149481_i" });
				try {

					if (!field.isAccessible()) {
						field.setAccessible(true);
					}

					if (target != null) {

						Field field1 = ReflectionHelper.findField(CPacketPlayer.class,
								new String[] { "pitch", "field_149473_f" });
						Field field2 = ReflectionHelper.findField(CPacketPlayer.class,
								new String[] { "yaw", "field_149476_e" });
						if (!field1.isAccessible()) {
							field1.setAccessible(true);
						}
						if (!field2.isAccessible()) {
							field2.setAccessible(true);
						}

						/*
						 * field1.setFloat(packet, Utils.getRotationsNeeded(target)[1]);
						 * field2.setFloat(packet, Utils.getRotationsNeeded(target)[0]);
						 * field.setBoolean(packet, true);
						 */

						// ChatUtils.message("ok");

					}
					if (field.getBoolean(packet) == true) {
						// ChatUtils.message("ok");
						RotationUtils.serverRotation = new RotationUtils.Rotation(((CPacketPlayer) packet).getYaw(0),
								((CPacketPlayer) packet).getPitch(0));
					}

				} catch (Exception e) {
					//////////System.out.println(e);
				}

			}
		}
		return super.onPacket(packet, side);
	}

	@Override
	public void onRenderGameOverlay(Text event) {
		ScaledResolution sr = new ScaledResolution(Wrapper.INSTANCE.mc());
		if (target != null) {
			float health = target.getHealth();
			if (health > 20) {
				health = 20;
			}
			int red = (int) Math.abs((((health * 5) * 0.01f) * 0) + ((1 - (((health * 5) * 0.01f))) * 255));
			int green = (int) Math.abs((((health * 5) * 0.01f) * 255) + ((1 - (((health * 5) * 0.01f))) * 0));
			Color customColor = new Color(red, green, 0).brighter();

			Wrapper.INSTANCE.fontRenderer().drawString("\2474\u2764\247f",
					sr.getScaledWidth() / 2 - Wrapper.INSTANCE.fontRenderer().getStringWidth("\2474\u2764\247f"),
					sr.getScaledHeight() / 2 - 15, customColor.getRGB());
			// Wrapper.INSTANCE.fontRenderer().drawString("\2474\u2764\247f"+(int)target.getHealth(),
			// sr.getScaledWidth()/2, sr.getScaledHeight()/2+5, customColor.getRGB());
			fontManager.getFont("SFM 7").drawString("" + (int) target.getHealth(), sr.getScaledWidth() / 2,
					sr.getScaledHeight() / 2 - 15, customColor.getRGB());
			Wrapper.INSTANCE.fontRenderer().drawString(target.getName(),
					sr.getScaledWidth() / 2 - Wrapper.INSTANCE.fontRenderer().getStringWidth("\2474\u2764\247f"),
					sr.getScaledHeight() / 2 - 24, 0xFFFFFFFF);

		}

		super.onRenderGameOverlay(event);
	}

	@SideOnly(Side.CLIENT)
	@Override
	public void onCameraSetup(CameraSetup event) {
		if (!(mode.getMode("AAC").isToggled() && event.getEntity() == Wrapper.INSTANCE.player() && target != null))
			return;

		float yaw = Utils.getRotationsNeeded(target)[0] - 180;
		float pitch = Utils.getRotationsNeeded(target)[1] + 10;
		facingCam = new float[] { Utils.getRotationsNeeded(target)[0], pitch };

		event.setYaw(yaw);
		event.setPitch(pitch);
		super.onCameraSetup(event);
	}

	@Override
	public void onClientTick(ClientTickEvent event) {
		if (mode.getMode("AAC").isToggled()) {
			phaseOne();
			phaseTwo();
			phaseFour();
		} else if (mode.getMode("Simple").isToggled()) {
			killAuraUpdate();
			killAuraAttack(target);
		} else if (mode.getMode("Multi").isToggled()) {
			MultiUpdate();
		}
		super.onClientTick(event);
	}

	void MultiUpdate() {
		EntityPlayerSP player = Wrapper.INSTANCE.player();
		World world = Wrapper.INSTANCE.world();
		double rangeSq = Math.pow(range.getValue(), 2);
		Stream<EntityLivingBase> stream = world.loadedEntityList.parallelStream()
				.filter(e -> e instanceof EntityLivingBase).map(e -> (EntityLivingBase) e)
				.filter(e -> !e.isDead && e.getHealth() > 0)
				.filter(e -> EntityUtils.getDistanceSq(player, e) <= rangeSq).filter(e -> e != player);

		Hack h = HackManager.getHack("Targets");

		if (!h.isToggledValue("Players"))
			stream = stream.filter(e -> !(e instanceof EntityPlayer));

		if (!h.isToggledValue("Mobs"))
			stream = stream.filter(e -> !(e instanceof IMob || e instanceof EntityAnimal));

		ArrayList<Entity> entities = stream.collect(Collectors.toCollection(() -> new ArrayList<>()));
		if (entities.isEmpty())
			return;

		int CPS = Utils.random((int) (minCPS.getValue().intValue()), (int) (maxCPS.getValue().intValue()));
		int r1 = Utils.random(1, 50), r2 = Utils.random(1, 60), r3 = Utils.random(1, 70);
		if (timer.hasReached((1000 + ((r1 - r2) + r3)) / CPS)) {
		for (Entity entity : entities) {
				Wrapper.INSTANCE.controller().attackEntity(player, entity);
			}
		player.swingArm(EnumHand.MAIN_HAND);
		timer.reset();
		}

	}

	@Override
	public void onPlayerTick(PlayerTickEvent event) {
		if (mode.getMode("AAC").isToggled())
			phaseThree(event);
		super.onPlayerTick(event);
	}

	void phaseOne() {
		if (target != null) {
			randomCenter = Utils.getRandomCenter(target.getEntityBoundingBox());
			facing = Utils.getSmoothNeededRotations(randomCenter, 100.0F, 100.0F);
		}
		killAuraUpdate();
		if (target != null)
			phaseOne = true;
	}

	void fakephaseOne(EventPlayerPre evnt) {
		if (target != null) {
			randomCenter = Utils.getRandomCenter(target.getEntityBoundingBox());
			facing = Utils.getSmoothNeededRotations(randomCenter, 100.0F, 100.0F);
		}
		killAuraUpdate();
		if (target != null)
			fakephaseOne = true;

	}

	void phaseTwo() {
		if (target == null || randomCenter == null || !phaseOne)
			return;
		if (facing[0] == Utils.getNeededRotations(randomCenter)[0]) {
			phaseOne = false;
			phaseTwo = true;
		}
	}

	void fakephaseTwo(EventPlayerPre event) {
		if (target == null || randomCenter == null || !fakephaseOne)
			return;
		if (facing[0] == Utils.getNeededRotations(randomCenter)[0]) {
			fakephaseOne = false;
			fakephaseTwo = true;
		}

	}

	void phaseThree(PlayerTickEvent event) {
		if (target == null || facing == null || event.player != Wrapper.INSTANCE.player())
			return;

		if (target.hurtTime <= target.maxHurtTime) {
			event.player.rotationYaw = facing[0];
			event.player.rotationPitch = facing[1];
			Wrapper.INSTANCE.player().rotationYawHead = facing[0];
		}
		if (!phaseTwo)
			return;

		event.player.rotationYaw = facing[0];
		event.player.rotationPitch = facing[1];
		Wrapper.INSTANCE.player().rotationYawHead = facing[0];

		phaseTwo = false;
		phaseThree = true;
	}

	void fakephaseThree(EventPlayerPre event) {
		if (target == null || facing == null)
			return;

		if (target.hurtTime <= target.maxHurtTime) {
			event.setYaw(facing[0]);
			event.setPitch(facing[1]);
			Wrapper.INSTANCE.player().rotationYawHead = facing[0];
			Wrapper.INSTANCE.player().setRotationYawHead(event.getYaw());
			Wrapper.INSTANCE.player().renderYawOffset = event.getYaw();
		}
		if (!fakephaseTwo)
			return;
		event.setYaw(facing[0]);
		event.setPitch(facing[1]);
		Wrapper.INSTANCE.player().setRotationYawHead(event.getYaw());
		Wrapper.INSTANCE.player().renderYawOffset = event.getYaw();
		Wrapper.INSTANCE.player().rotationYawHead = facing[0];

		fakephaseTwo = false;
		fakephaseThree = true;

	}

	void phaseFour() {
		if (target == null || randomCenter == null || !phaseThree
				|| facing[0] != Utils.getNeededRotations(randomCenter)[0]) {
			facingCam = null;
			return;
		}
		Entity rayCastEntity = RayCastUtils.rayCast((float) (range.getValue().floatValue() + 1.0F), facing[0],
				facing[1]);
		killAuraAttack(rayCastEntity == null ? target : (EntityLivingBase) rayCastEntity);
	}

	void fakephaseFour(EventPlayerPre event) {
		if (target == null || randomCenter == null || !fakephaseThree) {
			facingCam = null;
			return;
		}

		event.setPitch(Utils.getRotationsNeeded(target)[1]);
		event.setYaw(Utils.getRotationsNeeded(target)[0]);

		killAuraAttack(target);

	}

	void killAuraUpdate() {

		try{
			for (Object object : Wrapper.INSTANCE.world().loadedEntityList) {
				if (!(object instanceof EntityLivingBase))
					continue;
				EntityLivingBase entity = (EntityLivingBase) object;
				if (!check(entity)) {
					continue;
				}
				target = entity;
			}
		}catch(ConcurrentModificationException E){

		}

	}

	public void FakekillAuraAttack(EntityLivingBase entity, EventPlayerPre event) {
		if (entity == null) {
			AutoShield.block(false);
			blockstate = false;
			return;
		}
		event.setPitch(Utils.getRotationsNeeded(target)[1]);
		event.setYaw(Utils.getRotationsNeeded(target)[0]);
		Wrapper.INSTANCE.player().setRotationYawHead(event.getYaw());

		Wrapper.INSTANCE.player().renderYawOffset = event.getYaw();
		if (this.autoDelay.getValue()) {
			if (Wrapper.INSTANCE.player().getCooledAttackStrength(0) == 1) {
				processAttack(entity);
				target = null;
			}
		} else {
			int CPS = Utils.random((int) (minCPS.getValue().intValue()), (int) (maxCPS.getValue().intValue()));
			int r1 = Utils.random(1, 50), r2 = Utils.random(1, 60), r3 = Utils.random(1, 70);
			if (timer.isDelay((1000 + ((r1 - r2) + r3)) / CPS)) {
				processAttack(entity);
				timer.setLastMS();
				facingCam = null;
				target = null;
				phaseThree = false;
			}
		}
	}

	public void killAuraAttack(EntityLivingBase entity) {
		if (entity == null) {
			AutoShield.block(false);
			blockstate = false;
			return;
		}
		if (this.autoDelay.getValue()) {
			if (Wrapper.INSTANCE.player().getCooledAttackStrength(0) == 1) {
				processAttack(entity);
				target = null;
			}
		} else {
			int CPS = Utils.random((int) (minCPS.getValue().intValue()), (int) (maxCPS.getValue().intValue()));
			int r1 = Utils.random(1, 50), r2 = Utils.random(1, 60), r3 = Utils.random(1, 70);
			if (timer.isDelay((1000 + ((r1 - r2) + r3)) / CPS)) {
				processAttack(entity);
				timer.setLastMS();
				facingCam = null;
				// target = null;
				phaseThree = false;
				fakephaseThree = false;
				target = null;
			}
		}
	}

	public void BMultiAttack(EntityLivingBase entity) {
		if (entity == null) {
			AutoShield.block(false);
			blockstate = false;
			return;
		}
		if (this.autoDelay.getValue()) {
			if (Wrapper.INSTANCE.player().getCooledAttackStrength(0) == 1) {
				processAttack(entity);
				target = null;
			}
		} else {
			int CPS = Utils.random((int) (minCPS.getValue().intValue()), (int) (maxCPS.getValue().intValue()));
			int r1 = Utils.random(1, 50), r2 = Utils.random(1, 60), r3 = Utils.random(1, 70);
			if (timer.isDelay((1000 + ((r1 - r2) + r3)) / CPS)) {
				processAttack(entity);
				timer.setLastMS();
				facingCam = null;
				target = null;
				phaseThree = false;
			}
		}
	}

	private boolean hasSword() {
		return Wrapper.INSTANCE.player().inventory.getCurrentItem() != null
				&& Wrapper.INSTANCE.player().inventory.getCurrentItem().getItem() instanceof ItemSword;
	}

	public void processAttack(EntityLivingBase entity) {
		AutoShield.block(false);

		if (this.hasSword()) {
			this.unBlock();
		}

		if (!isInAttackRange(entity) || !ValidUtils.isInAttackFOV(entity, (int) (FOV.getValue().intValue())))
			return;
		EntityPlayerSP player = Wrapper.INSTANCE.player();
		float sharpLevel = EnchantmentHelper.getModifierForCreature(player.getHeldItemMainhand(),
				entity.getCreatureAttribute());
		if (this.packetReach.getValue()) {
			double posX = entity.posX - 3.5 * Math.cos(Math.toRadians(Utils.getYaw(entity) + 90.0f));
			double posZ = entity.posZ - 3.5 * Math.sin(Math.toRadians(Utils.getYaw(entity) + 90.0f));
			Wrapper.INSTANCE.sendPacket(new CPacketPlayer.PositionRotation(posX, entity.posY, posZ,
					Utils.getYaw(entity), Utils.getPitch(entity), player.onGround));
			Wrapper.INSTANCE.sendPacket(new CPacketUseEntity(entity));
			Wrapper.INSTANCE
					.sendPacket(new CPacketPlayer.Position(player.posX, player.posY, player.posZ, player.onGround));
		} else {
			if (autoDelay.getValue() || mode.getMode("Simple").isToggled())
				Utils.attack(entity);
			else
				Wrapper.INSTANCE.sendPacket(new CPacketUseEntity(entity));
		}
		Utils.swingMainHand();
		if (sharpLevel > 0.0f)
			player.onEnchantmentCritical(entity);
		AutoShield.block(true);

		// target=null;

	}

	public void MultiprocessAttack(EntityLivingBase entity) {
		AutoShield.block(false);
		blockstate = false;
		if (!isInAttackRange(entity) || !ValidUtils.isInAttackFOV(entity, (int) (FOV.getValue().intValue())))
			return;
		EntityPlayerSP player = Wrapper.INSTANCE.player();
		float sharpLevel = EnchantmentHelper.getModifierForCreature(player.getHeldItemMainhand(),
				entity.getCreatureAttribute());
		if (this.packetReach.getValue()) {
			double posX = entity.posX - 3.5 * Math.cos(Math.toRadians(Utils.getYaw(entity) + 90.0f));
			double posZ = entity.posZ - 3.5 * Math.sin(Math.toRadians(Utils.getYaw(entity) + 90.0f));
			Wrapper.INSTANCE.sendPacket(new CPacketPlayer.PositionRotation(posX, entity.posY, posZ,
					Utils.getYaw(entity), Utils.getPitch(entity), player.onGround));
			Wrapper.INSTANCE.sendPacket(new CPacketUseEntity(entity));
			Wrapper.INSTANCE
					.sendPacket(new CPacketPlayer.Position(player.posX, player.posY, player.posZ, player.onGround));
		} else {
			if (autoDelay.getValue() || mode.getMode("Multi").isToggled())
				Utils.attack(entity);
			else
				Wrapper.INSTANCE.sendPacket(new CPacketUseEntity(entity));
		}
		Utils.swingMainHand();
		if (sharpLevel > 0.0f)
			player.onEnchantmentCritical(entity);

	}

	boolean isPriority(EntityLivingBase entity) {
		return priority.getMode("Closest").isToggled() && ValidUtils.isClosest(entity, target)
				|| priority.getMode("Health").isToggled() && ValidUtils.isLowHealth(entity, target);
	}

	boolean isInAttackRange(EntityLivingBase entity) {
		return packetReach.getValue()
				? entity.getDistance(Wrapper.INSTANCE.player()) <= (float) (packetRange.getValue().floatValue())
				: entity.getDistance(Wrapper.INSTANCE.player()) <= (float) (range.getValue().floatValue());
	}

	public boolean check(EntityLivingBase entity) {
		if (entity instanceof EntityArmorStand) {
			return false;
		}
		if (ValidUtils.isValidEntity(entity)) {
			return false;
		}
		if (!ValidUtils.isNoScreen()) {
			return false;
		}
		if (entity == Wrapper.INSTANCE.player()) {
			return false;
		}
		if (entity.isDead) {
			return false;
		}
		if (entity.deathTime > 0) {
			return false;
		}
		if (ValidUtils.isBot(entity)) {
			return false;
		}
		if (!ValidUtils.isFriendEnemy(entity)) {
			return false;
		}
		if (!ValidUtils.isInvisible(entity)) {
			return false;
		}
		if (!ValidUtils.isInAttackFOV(entity, FOV.getValue().intValue())) {
			return false;
		}
		if (!isInAttackRange(entity)) {
			return false;
		}
		if (!ValidUtils.isTeam(entity)) {
			return false;
		}
		if (!ValidUtils.pingCheck(entity)) {
			return false;
		}
		if (!this.walls.getValue()) {
			if (!Wrapper.INSTANCE.player().canEntityBeSeen(entity)) {
				return false;
			}
		}
		if (!isPriority(entity)) {
			return false;
		}
		return true;
	}

	public void doBlock() {
		KeyBinding.setKeyBindState(Wrapper.INSTANCE.mc().gameSettings.keyBindUseItem.getKeyCode(), true);
	}

	public static boolean getBlockState() {
		return blockstate;
	}

	public static int randomNumber(int max, int min) {
		return Math.round((float) min + (float) Math.random() * (float) (max - min));
	}

	private void unBlock() {
		this.blockstate = false;
		Wrapper.INSTANCE.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.RELEASE_USE_ITEM,
				BlockPos.ORIGIN, EnumFacing.DOWN));
	}

	public static EntityLivingBase getTarget() {
		return target;
	}
}
